﻿using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Caching;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace isky.webapi
{
    public class XrmHttpClient : IDisposable
    {
        public string UserName { get; private set; }
        public string Password { get; private set; }

        public string OrgUrl { get; private set; }
        public string AdfsUrl { get; private set; }
        public string AuthUrl { get; private set; }

        private HttpClient httpClient;
        private WebRequestHandler handler;
        private double expires_in = 1; //小时
        private readonly string wa = "wsignin1.0";
        
        private readonly string wct = System.Web.HttpUtility.HtmlEncode(DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")); //"2018-07-11T03:18:41Z"
        private readonly string wauth = System.Web.HttpUtility.HtmlEncode("urn:oasis:names:tc:SAML:1.0:am:password");


        private readonly string AuthMethod = "FormsAuthentication";

        public XrmHttpClient(string userName, string passWord, string orgUrl, string adfsUrl, string authUrl)
        {
            this.UserName = userName;
            this.Password = passWord;
            this.OrgUrl = orgUrl;
            this.AdfsUrl = adfsUrl;
            this.AuthUrl = authUrl;
        }

        private async Task<WebRequestHandler> AcquireHttpClient()
        {
            string wtrealm = System.Web.HttpUtility.HtmlEncode(OrgUrl);
            string wctx = System.Web.HttpUtility.HtmlEncode($"rm=1&id=d5a8ecd7-6562-4144-bb3d-a475c64c7ffa&ru=https%3a%2f%2f{new Uri(OrgUrl).Host}%3a446%2fdefault.aspx");
            string adfsParams = $"?wa={wa}&wtrealm={System.Web.HttpUtility.HtmlEncode(wtrealm)}&wctx={System.Web.HttpUtility.HtmlEncode(wctx)}&wct={wct}&wauth={wauth}";

            string postAdfsUrl = string.Concat(AdfsUrl, adfsParams);
            var list = BuildCodeParams(UserName, Password);
            handler = new WebRequestHandler { AllowAutoRedirect = true };
            {
                httpClient = new HttpClient(handler);
                {
                    //第1次请求
                    httpClient.Timeout = new TimeSpan(0, 2, 0);
                    using (var response = httpClient.PostAsync(postAdfsUrl, new FormUrlEncodedContent(list)).Result)
                    {
                        var respContent = response.Content.ReadAsStringAsync().Result;
                        List<KeyValuePair<string, string>> formList = new List<KeyValuePair<string, string>>();
                        HtmlDocument htmlDocument = new HtmlDocument();

                        htmlDocument.Load(response.Content.ReadAsStreamAsync().Result, System.Text.Encoding.UTF8);
                        HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//input[@type='hidden']");
                        foreach (var itemNode in htmlNodes)
                        {
                            var inputname = itemNode.GetAttributeValue("name", string.Empty);
                            var inputval = itemNode.GetAttributeValue("value", string.Empty);
                            if (inputname.Equals("wresult"))
                            {
                                XmlDocument xmlDoc = new XmlDocument();
                                inputval = System.Web.HttpUtility.HtmlDecode(inputval);
                                xmlDoc.LoadXml(inputval);
                                XmlNamespaceManager nsp = new XmlNamespaceManager(xmlDoc.NameTable);
                                nsp.AddNamespace("t", "http://schemas.xmlsoap.org/ws/2005/02/trust");
                                nsp.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
                                XmlNodeList listNodeCreated = xmlDoc.DocumentElement.SelectNodes("/t:RequestSecurityTokenResponse/t:Lifetime/wsu:Created", nsp);
                                XmlNodeList listNodeExpires = xmlDoc.DocumentElement.SelectNodes("/t:RequestSecurityTokenResponse/t:Lifetime/wsu:Expires", nsp);
                 
                                DateTime createdDate;
                                DateTime ExpireDate;
                                DateTime.TryParseExact(listNodeCreated[0].InnerText, "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                                    System.Globalization.CultureInfo.InvariantCulture,
                                   System.Globalization.DateTimeStyles.None, out createdDate);

                                DateTime.TryParseExact(listNodeExpires[0].InnerText, "yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                                        System.Globalization.CultureInfo.InvariantCulture,
                                        System.Globalization.DateTimeStyles.None, out ExpireDate);
                                expires_in = ExecDateDiff(createdDate, ExpireDate);
                            }

                            formList.Add(new KeyValuePair<string, string>(inputname, inputval));
                        }
               
                        var authResp = await httpClient.PostAsync(AuthUrl, new FormUrlEncodedContent(formList));
                        respContent = await authResp.Content.ReadAsStringAsync();
                        return handler;
                    }
                }
            }
        }

        public WebRequestHandler GetWebRequestHandler()
        {
            ObjectCache oCache = MemoryCache.Default;
            if (oCache["WebRequestHandler"] as WebRequestHandler == null)
            {
                WebRequestHandler handler = this.AcquireHttpClient().Result;
                CacheItemPolicy policy = new CacheItemPolicy();
                policy.AbsoluteExpiration = DateTime.Now.AddHours(expires_in);
                oCache.Set("WebRequestHandler", handler, policy);
                return handler;
            }
            else {
                return oCache["WebRequestHandler"] as WebRequestHandler;
            }
        }

        public CookieContainer GetXrmCookieContainer()
        {
            ObjectCache oCache = MemoryCache.Default;
            if (oCache["XrmCookieContainer"] as CookieContainer == null)
            {
                WebRequestHandler handler = this.AcquireHttpClient().Result;
                CacheItemPolicy policy = new CacheItemPolicy();
                policy.AbsoluteExpiration = DateTime.Now.AddHours(expires_in);
                oCache.Set("WebRequestHandler", handler.CookieContainer, policy);
                return handler.CookieContainer;
            }
            else
            {
                return oCache["XrmCookieContainer"] as CookieContainer;
            }

        }

        private double ExecDateDiff(DateTime dateBegin, DateTime dateEnd)
        {
            TimeSpan ts1 = new TimeSpan(dateBegin.Ticks);
            TimeSpan ts2 = new TimeSpan(dateEnd.Ticks);
            TimeSpan ts3 = ts1.Subtract(ts2).Duration();
            //你想转的格式
            return ts3.TotalHours;
        }

        private void addCookieContainer(string cookiestr, CookieContainer myCookieContainer)
        {
            string[] cookieNameValue = cookiestr.Split('=');
            string value = cookieNameValue[1].Trim().ToString().Replace(",", "%2C");
            Cookie ck = new Cookie(cookieNameValue[0].Trim().ToString(), value);
            //ck.Domain = ".it168.com";
            myCookieContainer.Add(ck);
        }


        private List<KeyValuePair<string, string>> BuildCodeParams(string userName, string password)
        {
            var list = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string, string>("UserName",userName),
                new KeyValuePair<string, string>("Password",password),
                new KeyValuePair<string, string>("AuthMethod",AuthMethod)
            };
            return list;
        }

        public void Dispose()
        {
            if(handler!=null)
            {
                handler.Dispose();
            }
            if(httpClient!=null)
            {
                httpClient.Dispose();
            }
        }
    }
}
